#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define USAGE "Usage:\nchseg -s [seg str]"
#define READ_FIELD_SIZE 65534
#define MEMS_SIZE 256

static const int MAX_KEY_NUM = sizeof(int)*8;

static const char *code_str[] = {"gbk", "utf-8","utf8", NULL};
static const int code[] = {0,0,0};//{CODE_PAGE_GB18030, CODE_PAGE_UTF8, CODE_PAGE_UTF8};

struct config{
	int enc;
	int replace;
	char *srcfn;
	char *dstfn;
	int key_map;
	int del_map;
};


static char * mems[MEMS_SIZE] = {0};
static int m = 0;
static int f = 0;

static int init(){
	return 0;
}

static int destory(){
	return 0;
}

static int segment(char* dst, const char* src)
{
	if ('\0' == *src){
		*dst = '\0';
		return 0;
	}

	int left = 0;
	int i = 0;
	int j = 0;
	int src_len = strlen(src);
	unsigned char k = 0;
	for(i=0,j=0; i<src_len && j<2*src_len; i++,j++){
		k = (unsigned char)(src[i]);
	//	printf("k: %d\n",k);
		if(left == 0){
			if(k<128){			//0xxxxxxx
				left = 0;
			}else if(k>=192 && k<224){	//110xxxxx 10xxxxxx 
				left = 1;
			}else if(k>=224 && k<240){	//1110xxxx 10xxxxxx 10xxxxxx 
				left = 2;
			}else{
				break;
			}
			if(left > 0 && i > 0){
				dst[j++] = ' ';
			}
		}else if(left>0 && k>=128 && k<192){
			left --;
		}else{
			break;
		}
		dst[j] = src[i];
	}
	if(i<src_len && j<2*src_len){
		fprintf(stderr, "invalid char %u at %d\n",k,i);
	}else if(i<src_len && j==2*src_len){
		fprintf(stderr, "dst buf too small\n");
	}
	dst[j] = 0;
	return 0;
}

static int grow_field(char **fieldp, FILE *fp)
{
	char * field = (char *)malloc(READ_FIELD_SIZE+1);
	if(NULL == field){
		exit(100);
	}

	int ch = 0;
	int pos = 0;
	int count = 0;
	while(EOF!=(ch=fgetc(fp)) && '\n'!=ch && '\t'!=ch)
	{
		if(READ_FIELD_SIZE == pos){
			if(MEMS_SIZE-1 == count){
				fprintf(stderr, "field too long, modify the f**king code\n");
				break;
			}
			mems[count++] = field;
			field = (char *)malloc(READ_FIELD_SIZE+1);
			if(NULL == field){
				exit(100);
			}
			pos = 0;
		}
		*(field+(pos++)) = ch;
	}
	*(field+pos) = '\0';
	*fieldp = field;
	if(0 != count){
		mems[count] = field;
		field = (char *)malloc(READ_FIELD_SIZE*count + pos + 1);
		if(NULL == field){
			exit(100);
		}
		int i = 0;
		for(i=0; i<count; i++){
			memcpy(field+READ_FIELD_SIZE*i, mems[i],READ_FIELD_SIZE);
			free(mems[i]);
		}
		memcpy(field+READ_FIELD_SIZE*count,mems[count],pos);
		free(mems[count]);
		*(field+READ_FIELD_SIZE*count+pos) = '\0';
		*fieldp = field;
	}
	if('\t' == ch){
		return '\t';
	}else if('\n' == ch){
		return '\n';
	}else{
		return 0;
	}
}

static int drop_field(char *field){
	free(field);
}

static int on_file(char* srcfn, char* dstfn, int enc, int key_map, int del_map, int replace)
{
	FILE *srcfp = fopen(srcfn, "r");
	if(NULL == srcfp){
		perror("open src");
		return -1;
	}
	FILE *dstfp = fopen(dstfn, "w");
	if( NULL == dstfp){
		perror("open dst");
		return -2;
	}

	int ret = 0;
	int len = 0;
	int key_pos = 1;
	char * field = NULL;
	char * segfield = NULL;

	while(0 < (ret=grow_field(&field, srcfp)))
	{
		if(key_map & (1<<(key_pos-1))){
			if(!replace){
				fputs(field, dstfp);
				fputc('\t',dstfp);
			}
			segfield = (char *) malloc(2*strlen(field)+1);
			if(NULL == segfield){
				exit(100);
			}
			if(0 != segment(segfield, field)){
				free(segfield);
				drop_field(field);
				return -3;
			}
			fputs(segfield, dstfp);
			free(segfield);
		}else if(del_map & (1<<(key_pos-1))){
			key_pos ++;
			drop_field(field);
			continue;
		}else{
			fputs(field, dstfp);
		}
		key_pos ++;
		if('\t' == ret){
			fputc('\t',dstfp);
		}else if('\n' == ret){
			fputc('\n',dstfp);
			key_pos = 1;
		}
		drop_field(field);
	}

	fclose(srcfp);
	fclose(dstfp);

	return 0;	
}

int main(int argc,char** argv)
{
	int ret = 0;
	int c = 0;
	
	int key_pos = 0;
	struct config cfg;
	cfg.dstfn = cfg.srcfn = NULL;
//	cfg.enc = CODE_PAGE_UTF8;
	cfg.enc = 0;
	cfg.replace = cfg.key_map = cfg.del_map = 0;

	while ((c = getopt(argc, argv, "c:s:d:K:D:r")) != -1) 
	{
		int i = 0;
		switch (c)
		{
		case 'c':
			for(i = 0; NULL != code_str[i]; i++) 
			{
				if (0 == strcmp(optarg, code_str[i])) {
					cfg.enc = code[i];
					break;
				}
			}
			break;
		case 's':
			cfg.srcfn = optarg;
			break;
		case 'd':
			cfg.dstfn = optarg;
			break;
		case 'r':
			cfg.replace = 1;
			break;
		case 'K':
			key_pos = atoi(optarg);
			if(key_pos > MAX_KEY_NUM || key_pos < 1){
				fprintf(stderr, "invalde key pos: %d (max: %d)\n", key_pos, MAX_KEY_NUM);
				return 1;
			}
			cfg.key_map |= 1<<(key_pos-1);
		case 'D':
			key_pos = atoi(optarg);
			if(key_pos > MAX_KEY_NUM || key_pos < 1){
				fprintf(stderr, "invalde key pos: %d (max: %d)\n", key_pos, MAX_KEY_NUM);
				return 1;
			}
			cfg.del_map |= 1<<(key_pos-1);
			break;
		default:
		    fprintf(stderr, "Illegal argument \"%c\"\n%s\n", c, USAGE);
			return 2;
		}
	}
	if(NULL==cfg.srcfn || NULL==cfg.dstfn){
		fprintf(stderr, "no file defined\n");
		return 3;
	}
	printf("enc: %d\n""src: %s\n""dst: %s\n""key_map: %x\n", cfg.enc, cfg.srcfn, cfg.dstfn, cfg.key_map);
	

	ret = init();
	if(ret < 0){
		fprintf(stderr, "init err, ret=%d\n", ret);
		return 4;
	}
	ret = on_file(cfg.srcfn, cfg.dstfn, cfg.enc, cfg.key_map, cfg.del_map, cfg.replace);
	if(ret < 0){
		fprintf(stderr, "process err, ret=%d\n", ret);
		return 5;
	}
	destory();


	return 0;
}


